let s = Symbol()
console.log(typeof s) // symbol
// Symbol 函数可以接受一个字符串作为参数，表示对 Symbol 实例的描述，
// 主要是为了在控制台显示，或者转为字符串时，比较容易区分
let s1 = Symbol('foo')
let s2 = Symbol('bar')
console.log(s1) // Symbol(foo)
console.log(s2) // Symbol(bar)
console.log(s1.toString()) // Symbol(foo)
// 如果 Symbol 的参数是一个对象，就会调用该对象的 toString 方法
// 将其转为字符串，然后才生成一个 Symbol 值
const obj = {
  toString() {
    return 'abc'
  }
}
const sym = Symbol(obj)
console.log(sym) // Symbol(abc)
// Symbol 函数的参数只是表示对当前 Symbol 值的描述，因此相同参数的 Symbol 函数的返回值是不相等的
// 没有参数的情况
let a1 = Symbol()
let a2 = Symbol()
console.log(a1 === a2) // false
// 有参数的情况
let n1 = Symbol('foo')
let n2 = Symbol('foo')
console.log(n1 === n2) // false
// Symbol 值可以显式转为字符串
let sm = Symbol('My symbol')
console.log(String(sm)) // 'Symbol(My symbol)'
console.log(sm.toString()) // 'Symbol(My symbol)'
// Symbol 值也可以转为布尔值，但是不能转为数值
let b = Symbol()
console.log(Boolean(b)) // true
console.log(!b) // false
//由于每一个 Symbol 值都是不相等的，这意味着 Symbol 值可以作为标识符
// 用于对象的属性名，就能保证不会出现同名的属性。
// 这对于一个对象由多个模块构成的情况非常有用
// 能防止某一个键被不小心改写或覆盖。

let mySymbol = Symbol()
// 第一种写法
let aa = {}
aa[mySymbol] = 'Hello!'
// 第二种写法
let bb = {
  [mySymbol]: 'Hello!'
}
// 第三种写法
let cc = {}
Object.defineProperty(aa, mySymbol, { value: 'Hello!' })//等同于第一种
// 以上写法都得到同样结果
console.log(aa[mySymbol]) // "Hello!"
